<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <script>
      // 创建中间值,用于存储每次生成的订阅者,
      let WatchMid = null
      function Vue({ data }) {
        this._data = data
        for (const key in this._data) {
          Object.defineProperty(this, key, {
            get() {
              return this._data[key]
            },
            set(val) {
              this._data[key] = val
            },
          })
        }
        new Promu(this._data)
      }
      //批量创建发布者成员,对所有数据进行监听
      //   暂时无法检测数组数据修改,需要重新封装7大方法
      class Promu {
        constructor(obj) {
          // 传入值为数组就需要再进一步判断数组内部是否含有对象
          if (Array.isArray(obj)) {
            this.dereseveArray(obj)
          } else {
            this.deepor(obj)
          }
        }
        // 数组处理函数
        dereseveArray(arr) {
          arr.forEach((itme) => {
            if (typeof val === 'object' && val !== null) {
              this.deepor(obj)
            }
          })
        }
        // 递归判断对象内部是否函数有对象
        deepor(obj) {
          for (const key in obj) {
            if (typeof obj[key] === 'object' && obj[key] !== null) {
              this.deepor(obj[key])
            }
            this.observe(obj, key)
          }
        }
        observe(obj, key) {
          // 使用闭包的原理创建独立value,每个需要监视的值,都含有不同且独立的value值
          let value = obj[key]
          let mid = new Mideele()
          Object.defineProperty(obj, key, {
            get() {
              console.log('get')
              if (WatchMid !== null) {
                // 添加订阅者到当前数据的订阅中心管理者数组中
                mid.collect(WatchMid)
              }
              return value
            },
            set(val) {
              console.log('set')
              value = val
              // 如果传入值也为object让其也进行一次绑定
              if (typeof val === 'object' && val !== null) {
                new Promu(val)
              }
              // 通知所有数组中的订阅者,数据已经更新,可以去重新获取
              mid.affiche()
            },
          })
        }
      }
      class Mideele {
        constructor() {
          this.watchers = []
        }
        collect(watcher) {
          this.watchers.push(watcher)
        }
        affiche() {
          this.watchers.forEach((item) => {
            // 调用每个订阅者的更新
            item.updata()
          })
        }
      }
      class Watch {
        constructor(data, key) {
          //确定监视的数据
          //将其绑定到创建出的订阅者实例上
          this.data = data
          this.key = key
          WatchMid = this
          this.get()
          WatchMid = null
        }
        get() {
          return this.data[this.key]
        }
        updata() {
          console.log('数据已更新,当前新值为' + this.get())
        }
      }
      let vm = new Vue({
        data: {
          name: '张三',
          age: 18,
          arr: [
            'ssd',
            {
              name: 'ss',
            },
          ],
          obj: {
            sex: '男',
          },
        },
      })
      //   console.log(vm)
      //   console.log(vm.name)
      let wat = new Watch(vm, 'name')
      console.log(wat)
    </script>
  </body>
</html>
